﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MacroEngine.cs" company="Collaboris Ltd.">
//   Copyright (c) Collaboris Ltd. All rights Reserved.
// </copyright>
// <summary>
//   Defines the MacroEngine type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Collaboris.Saf.Engine
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;

    using Collaboris.Saf.Engine.Factory.Spring;
    using Collaboris.Saf.Engine.Report;
    using Collaboris.Saf.Exceptions;
    using Collaboris.Saf.Tracing;
    using Collaboris.Utils;

    /// <summary>
    /// The macro engine.
    /// </summary>
    public class MacroEngine : IMacro
    {
        #region Constants and Fields

        /// <summary>
        /// Stores a reference of the macro being used
        /// </summary>
        private MacroWrapper macroWrapper;

        /// <summary>
        /// Contains the instance of the factory object to be used by the macro
        /// </summary>
        private ISafFactory safFactory;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MacroEngine"/> class. 
        /// Initializes a new instance of the <see cref="T:System.Object"/> class.
        /// </summary>
        /// <param name="factory">
        /// The factory.
        /// </param>
        public MacroEngine(ISafFactory factory)
        {
            ValidationHelper.VerifyObjectArgument(factory, "factory");

            this.macroWrapper = new MacroWrapper(factory);

            this.safFactory = factory;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the trace switch.
        /// </summary>
        /// <value>The trace switch.</value>
        private static TraceSwitch TraceSwitch
        {
            get
            {
                return SafTraceHelper.TraceSwitch;
            }
        }

        /// <summary>
        /// Stores a reference for the macro being used
        /// </summary>
        private IMacro Macro
        {
            get
            {
                return this.macroWrapper.Macro;
            }
        }

        #endregion

        #region Public Methods
       
        
        /// <summary>
        /// The create.
        /// </summary>
        /// <param name="configLocations">
        /// The config locations.
        /// </param>
        /// <param name="ctx">
        /// The ctx.
        /// </param>
        /// <returns>
        /// </returns>
        public static IMacro Create(List<string> configLocations, SafContext ctx)
        {
            ISafFactory factory = new SpringFactory(configLocations, ctx);
            return new MacroEngine(factory);
        }

        /// <summary>
        /// Creates a new Macro instance based on the config location passed in.
        /// </summary>
        /// <param name="configLocation">The config locations.</param>
        /// <param name="ctx">The CTX.</param>
        /// <returns>A new Macro Instance</returns>
        public static IMacro Create(string configLocation, SafContext ctx)
        {
            ISafFactory factory = new SpringFactory(configLocation, ctx);
            return new MacroEngine(factory);
        }


        /// <summary>
        /// Creates a new Macro from a file.
        /// </summary>
        /// <param name="macroPath">The macro path.</param>
        /// <param name="ctx">The CTX.</param>
        /// <returns>A new Macro instance</returns>
        public static IMacro CreateFromFile(string macroPath, SafContext ctx)
        {
            return Create(macroPath, ctx);
        }

        #endregion

        #region Implemented Interfaces

        #region IMacro

        /// <summary>
        /// Gets or sets the action factory.
        /// </summary>
        /// <value>The action factory.</value>
        public ISafFactory ActionFactory
        {
            get
            {
                return this.safFactory;
            }
            set
            {
                this.safFactory = value;
            }
        }

        /// <summary>
        /// If an IAction in the macro fails (and StopOnDoException is true),
        /// then if AutoUndo is true, the "UnDo" method on each IAction 
        /// will be called in reverse order. (This includes the failing Action).
        /// </summary>
        /// <remarks>
        /// Note! In this situation if a seperate Undo Macro 
        /// has been designated it will not be used in this scenario.
        /// This mode effectively causes the "Do" Macrog to be executed in reverse
        /// order.
        /// </remarks>
        /// <value>The group.</value>
        public bool AutoUndo
        {
            get
            {
                return this.Macro.AutoUndo;
            }

            set
            {
                this.Macro.AutoUndo = value;
            }
        }

        /// <summary>
        /// Gets ID.
        /// </summary>
        /// <exception cref="NotImplementedException">
        /// </exception>
        public Guid TemplateID
        {
            get
            {
                return this.Macro.TemplateID;
            }
        }

        /// <summary>
        /// Gets the Macro Instance ID.
        /// </summary>
        /// <value>The ID.</value>
        public Guid InstanceID
        {
            get
            {
                return this.macroWrapper.Macro.InstanceID;
            }
        }

        /// <summary>
        /// Gets the context.
        /// </summary>
        /// <value>The context.</value>
        public SafContext Context
        {
            get
            {
                return this.macroWrapper.Macro.Context;
            }
        }

        public IDictionary<int, IAction> Actions
        {
            get
            {
                return this.macroWrapper.Macro.Actions;
            }
        }

        /// <summary>
        /// Gets the <see cref="Collaboris.Saf.Engine.IAction"/> at the specified index.
        /// </summary>
        /// <value></value>
        public IAction this[int index]
        {
            get
            {
                return this.Macro[index];
            }
        }

        /// <summary>
        /// Runs the macro
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <returns>
        /// </returns>
        public MacroReport Do(SafContext context)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "MacroEngine", "Do");

            MacroReport macroReport = null;

            // clear any instances of the macro that we might be using
            // so that dynamic property values can be reassigned.
            //this.macroWrapper.DeleteMacro();

            try
            {
                // run the macro
                macroReport = this.Macro.Do(context);

                return macroReport;
            }
            catch (ActionRunException exception)
            {
                TraceHelper.TraceException(TraceSwitch, "MacroEngine", "Do", exception);

                if (this.Macro.AutoUndo)
                {
                    this.Macro.Undo(context);
                }

                throw;
            }
            finally
            {
                TraceHelper.TraceMethodEnd(TraceSwitch, "MacroEngine", "Do");
            }
        }

        /// <summary>
        /// Undoes the macro
        /// </summary>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <returns>
        /// </returns>
        public MacroReport Undo(SafContext context)
        {
            TraceHelper.TraceMethodStart(TraceSwitch, "MacroEngine", "UnDo");

            try
            {
                // clear any instances of the macro that we might be using
                // so that dynamic property values can be reassigned.
                //this.macroWrapper.DeleteMacro();

                // run the macro
                return this.Macro.Undo(context);
            }
            finally
            {
                TraceHelper.TraceMethodEnd(TraceSwitch, "MacroEngine", "UnDo");
            }
        }

        /// <summary>
        /// Gets a dynamic Variable matching the key.
        /// </summary>
        /// <param name="variableName">Name of the variable.</param>
        /// <returns></returns>
        public object GetDynamicVariable(string variableName)
        {
            return this.Macro.GetDynamicVariable(variableName);
        }

        #endregion

        #endregion

        /// <summary>
        /// Manages instances of the macro class in a protected way that prevents
        /// macro instances to be accessed directly
        /// </summary>
        private class MacroWrapper
        {
            #region Constants and Fields

            /// <summary>
            /// Stores a reference for the macro being used
            /// </summary>
            private IMacro internalMacro;

            /// <summary>
            /// Stores an instance of the safFactory object that creates
            /// instances of a Macro
            /// </summary>
            private ISafFactory safFactory;

            #endregion

            #region Constructors and Destructors

            /// <summary>
            /// Initializes a new instance of the <see cref="MacroWrapper"/> class.
            /// </summary>
            /// <param name="safFactory">
            /// The saf factory.
            /// </param>
            public MacroWrapper(ISafFactory safFactory)
            {
                this.safFactory = safFactory;
            }

            #endregion

            #region Properties

            /// <summary>
            /// Stores a reference for the macro being used
            /// </summary>
            public IMacro Macro
            {
                get
                {
                    if (this.internalMacro == null)
                    {
                        this.internalMacro = this.safFactory.CreateMacro();
                    }

                    return this.internalMacro;
                }
            }

            #endregion

            #region Public Methods

            /// <summary>
            /// The delete macro.
            /// </summary>
            public void DeleteMacro()
            {
                this.internalMacro = null;
            }

            #endregion
        }

        /// <summary>
        /// Deletes the macro.
        /// </summary>
        public void DeleteMacro()
        {
            this.macroWrapper.DeleteMacro();
        }

        /*
        /// <summary>
        /// Gets the macro.
        /// </summary>
        /// <returns>
        /// </returns>
        private DynamicMacro GetMacro()
        {
            // Dynamically create the macro
            DynamicMacro macro = this.SafFactory.CreateMacro();

            // Ensure that there are no actions on the list
            // macro.ActionList.Clear();
            this.Macro = macro;
            return macro;
        }
        */
    }
}